74. 数据访问

74.1 配置数据源

自定义DataSource类型的@Bean可以覆盖默认设置,正如Section 24.7.1, “Third-party configuration”解释的那样,你可以很轻松的将它跟一系列Environment属性绑定:

@Bean
@ConfigurationProperties(prefix="datasource.fancy")
public DataSource dataSource() {
    return new FancyDataSource();
}
datasource.fancy.jdbcUrl=jdbc:h2:mem:mydb
datasource.fancy.username=sa
datasource.fancy.poolSize=30

Spring Boot也提供了一个工具类DataSourceBuilder用来创建标准的数据源。如果需要重用DataSourceProperties的配置,你可以从它初始化一个DataSourceBuilder

@Bean
@ConfigurationProperties(prefix="datasource.mine")
public DataSource dataSource(DataSourceProperties properties) {
    return properties.initializeDataSourceBuilder()
            // additional customizations
            .build();
}

在此场景中,你保留了通过Spring Boot暴露的标准属性,通过添加@ConfigurationProperties,你可以暴露在相应的命命名空间暴露其他特定实现的配置, 具体详情可参考'Spring Boot特性'章节中的Section 29.1, “Configure a DataSource”DataSourceAutoConfiguration类源码。

74.2 配置两个数据源

创建多个数据源和创建一个工作都是一样的,如果使用JDBC或JPA的默认自动配置,你需要将其中一个设置为@Primary(然后它就能被任何@Autowired注入获取)。

@Bean
@Primary
@ConfigurationProperties(prefix="datasource.primary")
public DataSource primaryDataSource() {
    return DataSourceBuilder.create().build();
}

@Bean
@ConfigurationProperties(prefix="datasource.secondary")
public DataSource secondaryDataSource() {
    return DataSourceBuilder.create().build();
}

74.3 使用Spring Data仓库

Spring Data可以为你的@Repository接口创建各种风格的实现。Spring Boot会为你处理所有事情,只要那些@Repositories接口跟你的@EnableAutoConfiguration类处于相同的包(或子包)。

对于很多应用来说,你需要做的就是将正确的Spring Data依赖添加到classpath下(JPA对应spring-boot-starter-data-jpa,Mongodb对应spring-boot-starter-data-mongodb),创建一些repository接口来处理@Entity对象,相应示例可参考JPA sampleMongodb sample

Spring Boot会基于它找到的@EnableAutoConfiguration来尝试猜测你的@Repository定义的位置。想要获取更多控制,可以使用@EnableJpaRepositories注解(来自Spring Data JPA)。

74.4 从Spring配置分离@Entity定义

Spring Boot会基于它找到的@EnableAutoConfiguration来尝试猜测@Entity定义的位置,想要获取更多控制可以使用@EntityScan注解,比如:

@Configuration
@EnableAutoConfiguration
@EntityScan(basePackageClasses=City.class)
public class Application {

    //...

}

74.5 配置JPA属性

Spring Data JPA已经提供了一些独立的配置选项(比如,针对SQL日志),并且Spring Boot会暴露它们,针对hibernate的外部配置属性也更多些,最常见的选项如下:

spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.hibernate.naming.physical-strategy=com.example.MyPhysicalNamingStrategy
spring.jpa.database=H2
spring.jpa.show-sql=true

ddl-auto配置是个特殊情况,它的默认设置取决于是否使用内嵌数据库(是则默认值为create-drop,否则为none)。当本地EntityManagerFactory被创建时,所有spring.jpa.properties.*属性都被作为正常的JPA属性(去掉前缀)传递进去了。

Spring Boot提供一致的命名策略,不管你使用什么Hibernate版本。如果使用Hibernate 4,你可以使用spring.jpa.hibernate.naming.strategy进行自定义;Hibernate 5定义一个PhysicalImplicit命名策略:Spring Boot默认配置SpringPhysicalNamingStrategy,该实现提供跟Hibernate 4相同的表结构。如果你情愿使用Hibernate 5默认的,可以设置以下属性:

spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

具体详情可参考HibernateJpaAutoConfigurationJpaBaseConfiguration

74.6 使用自定义EntityManagerFactory

为了完全控制EntityManagerFactory的配置,你需要添加一个名为entityManagerFactory@Bean,Spring Boot自动配置会根据是否存在该类型的bean来关闭它的实体管理器(entity manager)。

74.7 使用两个EntityManagers

即使默认的EntityManagerFactory工作的很好,你也需要定义一个新的EntityManagerFactory,因为一旦出现第二个该类型的bean,默认的将会被关闭。为了轻松的实现该操作,你可以使用Spring Boot提供的EntityManagerBuilder,或者如果你喜欢的话可以直接使用来自Spring ORM的LocalContainerEntityManagerFactoryBean

示例:

// add two data sources configured as above

@Bean
public LocalContainerEntityManagerFactoryBean customerEntityManagerFactory(
        EntityManagerFactoryBuilder builder) {
    return builder
            .dataSource(customerDataSource())
            .packages(Customer.class)
            .persistenceUnit("customers")
            .build();
}

@Bean
public LocalContainerEntityManagerFactoryBean orderEntityManagerFactory(
        EntityManagerFactoryBuilder builder) {
    return builder
            .dataSource(orderDataSource())
            .packages(Order.class)
            .persistenceUnit("orders")
            .build();
}

上面的配置靠自己基本可以运行,想要完成作品你还需要为两个EntityManagers配置TransactionManagers。其中的一个会被Spring Boot默认的JpaTransactionManager获取,如果你将它标记为@Primary。另一个需要显式注入到一个新实例。或你可以使用一个JTA事物管理器生成它两个。

如果使用Spring Data,你需要相应地需要配置@EnableJpaRepositories

@Configuration
@EnableJpaRepositories(basePackageClasses = Customer.class,
        entityManagerFactoryRef = "customerEntityManagerFactory")
public class CustomerConfiguration {
    ...
}

@Configuration
@EnableJpaRepositories(basePackageClasses = Order.class,
        entityManagerFactoryRef = "orderEntityManagerFactory")
public class OrderConfiguration {
    ...
}

74.8 使用普通的persistence.xml

Spring不要求使用XML配置JPA提供者(provider),并且Spring Boot假定你想要充分利用该特性。如果你倾向于使用persistence.xml,那你需要定义你自己的id为entityManagerFactoryLocalEntityManagerFactoryBean类型的@Bean,并在那设置持久化单元的名称,默认设置可查看JpaBaseConfiguration

74.9 使用Spring Data JPA和Mongo仓库

Spring Data JPA和Spring Data Mongo都能自动为你创建Repository实现。如果它们同时出现在classpath下,你可能需要添加额外的配置来告诉Spring Boot你想要哪个(或两个)为你创建仓库。最明确地方式是使用标准的Spring Data @Enable*Repositories,然后告诉它你的Repository接口的位置(此处*即可以是Jpa,也可以是Mongo,或者两者都是)。

这里也有spring.data.*.repositories.enabled标志,可用来在外部配置中开启或关闭仓库的自动配置,这在你想关闭Mongo仓库但仍使用自动配置的MongoTemplate时非常有用。

相同的障碍和特性也存在于其他自动配置的Spring Data仓库类型(Elasticsearch, Solr),只需要改变对应注解的名称和标志。

74.10 将Spring Data仓库暴露为REST端点

Spring Data REST能够将Repository的实现暴露为REST端点,只要该应用启用Spring MVC。Spring Boot暴露一系列来自spring.data.rest命名空间的有用属性来定制化RepositoryRestConfiguration,你可以使用RepositoryRestConfigurer提供其他定制。

74.11 配置JPA使用的组件

如果想配置一个JPA使用的组件,你需要确保该组件在JPA之前初始化。组件如果是Spring Boot自动配置的,Spring Boot会为你处理。例如,Flyway是自动配置的,Hibernate依赖于Flyway,这样Hibernate有机会在使用数据库前对其进行初始化。

如果自己配置组件,你可以使用EntityManagerFactoryDependsOnPostProcessor子类设置必要的依赖,例如,如果你正使用Hibernate搜索,并将Elasticsearch作为它的索引管理器,这样任何EntityManagerFactory beans必须设置为依赖elasticsearchClient bean:

/**
 * {@link EntityManagerFactoryDependsOnPostProcessor} that ensures that
 * {@link EntityManagerFactory} beans depend on the {@code elasticsearchClient} bean.
 */
@Configuration
static class ElasticsearchJpaDependencyConfiguration
        extends EntityManagerFactoryDependsOnPostProcessor {

    ElasticsearchJpaDependencyConfiguration() {
        super("elasticsearchClient");
    }

}